
registerJQueryFunction(function(selector){

		/**
		 * Disable regular form submition.
		 */
		selector('form').submit(function(){
				return false;
		});

		/**
		 * Makes any HTML element with "action" attriubte
		 * send AJAX requests when clicked.
		 */
		selector('[action]:not([no_send_on_click])').click(function(){
				var these=$(this);
				var filterString=$.unique(these.map(function(){
							if($(this).is('[trigger_along]'))
								return $(this).attr('trigger_along').split(' ');
							else
								return null;
				})).map(function(){return '[autotrigger~='+this+']';}).toArray().join(',');
				if(''!=filterString){
					$.merge(these,$(filterString).filter(function(){return !these.is($(this));}));
				}
				these.sendAjaxForm('action');
		});

		/**
		 * Make file loader input element.
		 */
		selector('input[type=file]').each(function(){
				var fileElement=$(this);

				var progressDisplay=$('<span></span>');
				fileElement.after(progressDisplay);

				var fileReader=new FileReader();
				fileElement.data('fileReader',fileReader);
				fileReader.onprogress=function(event){
					progressDisplay.html(renderEjs('widgets/progress_display',{
								progress:(event.loaded/event.total)
					}));
				};
				fileReader.onloadend=function(event){
					progressDisplay.html('');
					var thumbnail=$('<img/>');
					progressDisplay.append(thumbnail);
					thumbnail[0].src=event.target.result;
					thumbnail.addClass('uploaded-image-thumbnail');
				}

				fileElement.change(function(){
						fileReader.readAsDataURL(this.files[0]);
				});
		});
});

var formArrayPattern=new RegExp("(\\w*)\\[]");

/**
 * Convert an HTML form to JSON.
 */
function formToJson(form){
	var result={};
	var formData=form.serializeArray();
	for(i in formData){
		var matches;
		if(matches=formArrayPattern.exec(formData[i].name)){
			result[matches[1]]=result[matches[1]]||[];
			result[matches[1]].push(formData[i].value);
		}else{
			result[formData[i].name]=formData[i].value;
		}
	}
	form.find('input[type=file]').each(function(){
			result[$(this).attr('name')+'-data']=$(this).data('fileReader').result;
	});
	form.find('div[type=datepicker]').each(function(){
			result[$(this).attr('name')]=$(this).val();
	});
	return result;
}

/**
 * Collect form data and put it in a `form_data` field of a new object.
 */
function collectFormData(){
	return {
		form_data:formToJson($(this).closest('form'))
	}
}

/**
 * Send an HTML form using AJAX.
 */
$.fn.sendAjaxForm=function(actionAttribute){
	var errors=$(this).closest('form').collectAllErrors();
	if(!$.isEmptyObject(errors)){
		errorsAlert(errors);
		return false;
	}
	actionAttribute=actionAttribute||'action';
	var requests=[];
	$(this).each(function(){
			var thisElement=$(this);
			if(thisElement.attr(actionAttribute)){
				var controller_method=thisElement.controllerAndMethod(actionAttribute);
				var controller=controller_method[0];
				var method=controller_method[1];
				var dataToSend={
					controller:controller,
					method:method,
					params:{form_data:formToJson(thisElement.closest('form'))}
				};
				thisElement.closest('[store_last_query]').data('last_query',dataToSend);
				requests.push({
						data:dataToSend,
						success:function(result){
							thisElement.runCallbackAfterAjax(result);
						}
				});
			}else if(thisElement.data('last_query')){
				requests.push({
						data:thisElement.data('last_query'),
						success:function(result){
							thisElement.runCallbackAfterAjax(result);
						}
				});
			}
	});
	makeMultipleAjaxRequests(requests);
}

/**
 * Run a callback after receiving AJAX response.
 */
$.fn.runCallbackAfterAjax=function(result){
	$(this).each(function(){
			var thisElement=$(this);
			if(thisElement.is('[close_parent]')){
				thisElement.closest(thisElement.attr('close_parent')).remove();
			}
			if(thisElement.is('[render_parent]')){
				var targetParent=thisElement.closest(thisElement.attr('render_parent'));
				if(thisElement.is('[render_parent_ejs]')){
					targetParent.htmlJQ(renderEjs(thisElement.attr('render_parent_ejs'),result));
				}else{
					targetParent.htmlJQ(renderEjs(result));
				}
			}
			if(thisElement.is('[data-callback]')){
				var callbacks=getFunctions(thisElement.data('callback'));
				for(i in callbacks){
					if(null!=callbacks[i]){
						callbacks[i].call(thisElement,result);
					}
				}
			}
	});
}
